#include "ColorRenderTarget.h"
#include "../../Core/CoreGlobals.h"
#include "../../Utils/LogUtil.h"
#include "../../Templates/NewObject.h"
#include "../RenderState.h"
#include "../../Game/Process/RenderProcess.h"

ColorRenderTarget::BuilderDetails::BuilderDetails()
{
	isMultiple = false;

	format = VK_FORMAT_R8G8B8A8_UNORM;
	width = GRenderState->GetWidth();
	height = GRenderState->GetHeight();
	// samples = GVulkanRHI->GetMsaaSamples();
	samples = VK_SAMPLE_COUNT_1_BIT;
	usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
	sharingMode = VK_SHARING_MODE_EXCLUSIVE;

	loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
	storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
	stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
	stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
	initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
	// If you want to make this texture to be read by pipeline of other render pass , you can set finalLayout to VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL and you dont have to transfer layout by youself, and dont forget to set the storeOp to VK_ATTACHMENT_STORE_OP_STORE
	finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;

	clearColorValue = { 0.f, 0.f, 0.f, 0.f };
}

ColorRenderTarget::Builder::Builder() noexcept = default;
ColorRenderTarget::Builder::~Builder() noexcept = default;
ColorRenderTarget::Builder::Builder(ColorRenderTarget::Builder const& rhs) noexcept = default;
ColorRenderTarget::Builder::Builder(ColorRenderTarget::Builder && rhs) noexcept = default;
ColorRenderTarget::Builder& ColorRenderTarget::Builder::operator=(ColorRenderTarget::Builder const& rhs) noexcept = default;
ColorRenderTarget::Builder& ColorRenderTarget::Builder::operator=(ColorRenderTarget::Builder && rhs) noexcept = default;

ColorRenderTarget::Builder& ColorRenderTarget::Builder::SetMultiple(bool isMultiple) noexcept
{
	mImpl->isMultiple = isMultiple;
	return *this;
}

ColorRenderTarget::Builder& ColorRenderTarget::Builder::SetFormat(VkFormat format) noexcept
{
	mImpl->format = format;
	return *this;
}

ColorRenderTarget::Builder& ColorRenderTarget::Builder::SetWidth(uint32 width) noexcept
{
	mImpl->width = width;
	return *this;
}

ColorRenderTarget::Builder& ColorRenderTarget::Builder::SetHeight(uint32 height) noexcept
{
	mImpl->height = height;
	return *this;
}

ColorRenderTarget::Builder& ColorRenderTarget::Builder::SetSamples(VkSampleCountFlagBits samples) noexcept
{
	assert(samples <= GVulkanRHI->GetMsaaSamples());
	mImpl->samples = samples;
	return *this;
}

ColorRenderTarget::Builder& ColorRenderTarget::Builder::SetUsage(VkImageUsageFlags usage) noexcept
{
	// If use for msaa to make the resolve texture, need VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT
	// If use for input attachment, need VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT
	// Color texture need to contain VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
	assert(usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
	mImpl->usage = usage;
	return *this;
}

ColorRenderTarget::Builder& ColorRenderTarget::Builder::SetSharingMode(VkSharingMode sharingMode) noexcept
{
	mImpl->sharingMode = sharingMode;
	return *this;
}

ColorRenderTarget::Builder& ColorRenderTarget::Builder::AddQueueFamily(VkQueueFlagBits queueFlag) noexcept
{
	mImpl->queueFamilys.push_back(queueFlag);
	return *this;
}

ColorRenderTarget::Builder& ColorRenderTarget::Builder::SetLoadOp(VkAttachmentLoadOp loadOp) noexcept
{
	mImpl->loadOp = loadOp;
	return *this;
}

ColorRenderTarget::Builder& ColorRenderTarget::Builder::SetStoreOp(VkAttachmentStoreOp storeOp) noexcept
{
	mImpl->storeOp = storeOp;
	return *this;
}

ColorRenderTarget::Builder& ColorRenderTarget::Builder::SetStencilLoadOp(VkAttachmentLoadOp stencilLoadOp) noexcept
{
	mImpl->stencilLoadOp = stencilLoadOp;
	return *this;
}

ColorRenderTarget::Builder& ColorRenderTarget::Builder::SetStencilStoreOp(VkAttachmentStoreOp stencilStoreOp) noexcept
{
	mImpl->stencilStoreOp = stencilStoreOp;
	return *this;
}

ColorRenderTarget::Builder& ColorRenderTarget::Builder::SetInitialLayout(VkImageLayout initialLayout) noexcept
{
	mImpl->initialLayout = initialLayout;
	return *this;
}

ColorRenderTarget::Builder& ColorRenderTarget::Builder::SetFinalLayout(VkImageLayout finalLayout) noexcept
{
	mImpl->finalLayout = finalLayout;
	return *this;
}

ColorRenderTarget::Builder& ColorRenderTarget::Builder::SetClearColorValue(VkClearColorValue clearColorValue) noexcept
{
	mImpl->clearColorValue = clearColorValue;
	return *this;
}

RefCountPtr<ColorRenderTarget> ColorRenderTarget::Builder::Build()
{
	assert(IsInRenderThread());
	return RefCountPtr<ColorRenderTarget>(NewObject<ColorRenderTarget>(*mImpl));
}

RefCountPtr<ColorRenderTarget> ColorRenderTarget::Builder::Build(RenderProcess * process, const std::string & name)
{
	assert(IsInRenderThread());
	ColorRenderTarget* colorRenderTarget = NewObject<ColorRenderTarget>(*mImpl);
	process->AddTexture(name, colorRenderTarget);
	return RefCountPtr<ColorRenderTarget>(colorRenderTarget);
}

void ColorRenderTarget::ReleaseResource()
{
	if (IsInRenderThread())
	{
		if (mProcess != nullptr)
		{
			mProcess->RemoveTexture(mName, this);
		}
		DeleteObject<ColorRenderTarget>(this);
	}
	else
	{
		AsyncTask(GRenderThreadId, [this]() {
			if (mProcess != nullptr)
			{
				mProcess->RemoveTexture(mName, this);
			}
			DeleteObject<ColorRenderTarget>(this);
			});
	}
}

ColorRenderTarget::ColorRenderTarget(BuilderDetails & builderDetails)
{
	mIsMultiple = builderDetails.isMultiple;
	mIsRenderTarget = true;
	mRenderTargetType = RenderTargetType::Color;

	mFlags = 0;
	mImageType = VK_IMAGE_TYPE_2D;
	mFormat = builderDetails.format;
	mExtent = { builderDetails.width, builderDetails.height, 1 };
	mUsage = builderDetails.usage;
	mMipLevels = 1;
	mArrayLayers = 1;
	mSamples = builderDetails.samples;
	mTiling = VK_IMAGE_TILING_OPTIMAL;
	mSharingMode = builderDetails.sharingMode;
	mQueueFamilys.assign(builderDetails.queueFamilys.begin(), builderDetails.queueFamilys.end());
	if (mQueueFamilys.size() > 1)
	{
		// Check if the queueFamilys is same
		bool isDifferent = false;
		const uint32 standQueueFamily = GVulkanRHI->GetQueueFamily(mQueueFamilys[0]);
		for (uint32 i = 1; i < mQueueFamilys.size(); ++i)
		{
			if (GVulkanRHI->GetQueueFamily(mQueueFamilys[i]) != standQueueFamily)
				isDifferent = true;
		}

		if (isDifferent)
			mSharingMode = VK_SHARING_MODE_CONCURRENT; // multi queue
		else
			mSharingMode = VK_SHARING_MODE_EXCLUSIVE;
	}
	else
	{
		assert(mSharingMode == VK_SHARING_MODE_EXCLUSIVE);
	}
	mImageLayouts.resize(GetFrameCount(), VK_IMAGE_LAYOUT_UNDEFINED);
	mMemoryProperty = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;

	mViewType = VK_IMAGE_VIEW_TYPE_2D;
	mComponents = { VK_COMPONENT_SWIZZLE_R , VK_COMPONENT_SWIZZLE_G , VK_COMPONENT_SWIZZLE_B , VK_COMPONENT_SWIZZLE_A };
	mSubresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0 , 1 };

	mAttachmentDescription = {};
	mAttachmentDescription.format = mFormat;
	mAttachmentDescription.samples = mSamples;
	mAttachmentDescription.loadOp = builderDetails.loadOp;
	mAttachmentDescription.storeOp = builderDetails.storeOp;
	mAttachmentDescription.stencilLoadOp = builderDetails.stencilLoadOp;
	mAttachmentDescription.stencilStoreOp = builderDetails.stencilStoreOp;
	mAttachmentDescription.initialLayout = builderDetails.initialLayout;
	mAttachmentDescription.finalLayout = builderDetails.finalLayout;

	mClearValue = {};
	mClearValue.color = builderDetails.clearColorValue;

	mImages.resize(GetFrameCount(), VK_NULL_HANDLE);
	mImageMemorys.resize(GetFrameCount(), VK_NULL_HANDLE);
	mImageViews.resize(GetFrameCount(), VK_NULL_HANDLE);

	VkCommandBuffer commandBuffer = GRenderState->BeginSingleCommand();

	for (uint32 i = 0; i < GetFrameCount(); ++i)
	{
		GVulkanRHI->CreateImage(mImages[i], mImageMemorys[i], mFlags, mExtent, mUsage, mFormat, mMipLevels, mMemoryProperty, mSamples, mImageType, mImageLayouts[i], mArrayLayers, mTiling, mSharingMode, mQueueFamilys);

		GVulkanRHI->CreateImageView(mImageViews[i], mImages[i], mFormat, mViewType, mSubresourceRange, mComponents);
	}

	GRenderState->FlushSingleCommand(commandBuffer);
}

